//////////////////////////////////////////////////////////////////////
// Frustum.cpp
//
// WANG Yangzihao
// slashspirit@gmail.com
//
// Copyright (C) WANG Yangzihao, 2011, All rights reserved.
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
//  Brief Description
//
//	Frustum class implementation file
//
//////////////////////////////////////////////////////////////////////
#include "grflib_stdafx.h"
#include <boost/typeof/typeof.hpp>
#include <boost/foreach.hpp>
#include "../include/Frustum.h"
namespace grflib 
{
	namespace engine 
	{
		namespace core
		{
			// Using the implementation from the paper:
			// Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix
			void CFrustum::ClipMatrix(D3DXMATRIX const & clip)
			{
				const float p1[4] = {clip._14+clip._11, clip._24+clip._21, clip._34+clip._31, clip._44+clip._41}; //left
				const float p2[4] = {clip._14-clip._11, clip._24-clip._21, clip._34-clip._31, clip._44-clip._41}; //right
				const float p3[4] = {clip._14-clip._12, clip._24-clip._22, clip._34-clip._32, clip._44-clip._42}; //top
				const float p4[4] = {clip._14+clip._12, clip._24+clip._22, clip._34+clip._32, clip._44+clip._42}; //bottom
				const float p5[4] = {clip._13, clip._23, clip._33, clip._43}; //near
				const float p6[4] = {clip._14-clip._13, clip._24-clip._23, clip._34-clip._33, clip._44-clip._43}; //far

				m_planes[0].SetValue(p1);
				m_planes[1].SetValue(p2);
				m_planes[2].SetValue(p3);
				m_planes[3].SetValue(p4);
				m_planes[4].SetValue(p5);
				m_planes[5].SetValue(p6);

				BOOST_FOREACH(BOOST_TYPEOF(m_planes)::reference plane, m_planes)
				{
					plane.Normalize();
				}

				//  build a bit-field that will tell us the indices for the nearest and farthest vertices from each plane...
				for (int i = 0; i < 6; ++ i)
				{
					m_vertex_lut[i] = ((m_planes[i].a() < 0) ? 1 : 0) | ((m_planes[i].b() < 0) ? 2 : 0) | ((m_planes[i].c() < 0) ? 4 : 0);
				}


			}

			CFrustum::VIS CFrustum::Visible(engine::core::BBox<float> const & box) const
			{
				bool intersect = false;
				for (int i = 0; i < 6; ++ i)
				{
					int const n = m_vertex_lut[i];

					// v1 is diagonally opposed to v0
					float3 v0((n & 1) ? box.m_min[0] : box.m_max[0], (n & 2) ? box.m_min[1] : box.m_max[1], (n & 4) ? box.m_min[2] : box.m_max[2]);
					float3 v1((n & 1) ? box.m_max[0] : box.m_min[0], (n & 2) ? box.m_max[1] : box.m_min[1], (n & 4) ? box.m_max[2] : box.m_min[2]);

					if (m_planes[i].a()*v0._x+
						m_planes[i].b()*v0._y+
						m_planes[i].c()*v0._z+
						m_planes[i].d()< 0)
					{
						return VIS_NO;
					}
					if (m_planes[i].a()*v1._x+
						m_planes[i].b()*v1._y+
						m_planes[i].c()*v1._z+
						m_planes[i].d()< 0)
					{
						intersect = true;
					}

					// The following code comes from Real-Time Rendering, 3rd version
					// It works too.
					/*float e = box.m_halfsize[0]*abs(m_planes[i].a())
					+box.m_halfsize[1]*abs(m_planes[i].b())
					+box.m_halfsize[2]*abs(m_planes[i].c());
					float s = m_planes[i].a()*box.m_center[0]+
					m_planes[i].b()*box.m_center[1]+
					m_planes[i].c()*box.m_center[2]+
					m_planes[i].d();
					if ( s - e < 0 ) intersect = true;
					if ( s + e < 0 ) return VIS_NO;*/
				}

				return intersect ? VIS_PART : VIS_YES;
			}
		}
	}
} //namespaces